import Material from '../shaders/CustomStandard.js';

//import LightmapST from '../assets/lightmapST.json';

var camera, scene, renderer, controls;
var mesh;
var loader;

var clock = new THREE.Clock();

var blocker = document.getElementById('blocker');

var currentScene = null;

var socket;
//var serverIP = "13.115.12.4";
var serverIP = "123.56.4.70";
var serverPort = "6666";
var webService = "Lcrs";
var isServerConnected = false;

var image = new Image();
var pixels = null;

var radianceWidth = 512, radianceHeight = 512;
var screenWidth = window.innerWidth;
var screenHeight = window.innerHeight;
var dynSpotLight = null;

var lastCameraPosition = new THREE.Vector3();
var lastCameraRotation = new THREE.Quaternion();

var lastLightPosition = new THREE.Vector3();
var lastLightRotation = new THREE.Quaternion();

var DiffEPS = 0.000001;

var isNeedToSync = false;

var isNeedToResetLastGT = false;

var recSequenceNum = 0;

var effectComposer;
var blendPass;
var renderTarget;

var guiPars = { direct: 1.0, indirect:1.0, bounce: 0.8, lcrsEnable: true, dynLightGreen: true, dynLightRed: true, lightmapOn: true, PointLight: 1.0, PlantX: 0, PlantY: 0, PlantZ: 0,
                roughness:1, metalness:0,
                onReCalc:onClickedReCalcLightmap, };
var lightObj;
var cameraForward = new THREE.Vector3();

var lightForward = new THREE.Vector3();

var objBox3;

var centerOffset = new THREE.Vector3();

var MaxRadianceSizeConst = 512;
var varMaxRadianceSize = 512;
var MaxsampleRate = 24;

var samleDuration = 1.0 / MaxsampleRate;

var PowerOfTwoArray = null;

var lastSampleTimestamp = 0;

var MaxProgressiveTime = 5;

var progressiveCounter = 1;

var dynLightGreen = null;
var dynLightRed = null;

var dynLightGreenTarget = null;
var dynLightRedTarget = null;

var blackImage = null;

var frameCounter = 0;

var resetTimestamps = new Map();

var lastReqFrameCounter = 0;
var sceneObj;
//TODO： 当前只支持一个lightmap
var LightmapTex;
var Pointlights = new Array();
var PointlightsDefaultIntensity = new Array();
var StateCode =
    {
        Common: 0,
        C2S_AttributeStream: 1,
        S2C_RadianceStream: 2,
        C2S_DGI_Init: 3,
        S2C_Json_LightmapST: 4,
        S2C_Json_LightmapArray: 5,
        C2S_Json_UpdateObjectPosition: 6,
        C2D_DGI_Recalc:7
    };
var lightmapArray = null;
var raycaster = new THREE.Raycaster();
var transformControl;
var enableTransformControl = false;
var mouse = new THREE.Vector2();
var raycastObj;
var oldPosValue = null;
var isUpdatingLightmap = false;
var updateTimeOut;
var tick=0;
var createLightGizmos = function()
{
    var geometry = new THREE.Geometry();
    geometry.vertices.push(new THREE.Vector3(0,0,0));
    var sprite = new THREE.TextureLoader().load( "assets/light_icon.png" );
    var  material = new THREE.PointsMaterial( { size: 35, sizeAttenuation: false, map: sprite, alphaTest: 0.5, transparent: true } );
    raycastObj = geometry;
    return new THREE.Points( geometry, material );  
}
var loadScene = function (sceneName) {
    currentScene = sceneName;

    blocker.style.opacity = 1.0;

    // document.body.style.background = 'radial-gradient(ellipse at 50% bottom,rgba(71,77,83,1) 0,rgba(35,38,40,1) 53%,rgba(17,18,19,1) 100%)';

    var manager = new THREE.LoadingManager();
    manager.onProgress = function (item, loaded, total) {

        console.log(item, loaded, total);

    };

    var texture = new THREE.Texture();

    var onProgress = function (xhr) {
        if (xhr.lengthComputable) {
            var percentComplete = xhr.loaded / xhr.total * 100;
            console.log(Math.round(percentComplete, 2) + '% downloaded');
        }
    };

    var onError = function (xhr) {
        console.log(xhr);
    };


    var loader = new THREE.ImageLoader(manager);
    loader.load('InteriorOffice/SimpleOffice_Texture.png', function (image) {
        texture.image = image;
        texture.needsUpdate = true;
    });
    LightmapTex = new THREE.Texture();
    //loader.load("assets/Lightmap0.png", function (image) {
    //    {
    //        LightmapTex.image = image;
    //        LightmapTex.needsUpdate = true;
    //    }
    // });
    // model
    var lightmapMat = new THREE.ShaderMaterial({
        uniforms: Material.CustomStandard.uniforms,
        vertexShader: Material.CustomStandard.vertexShader,
        fragmentShader: Material.CustomStandard.fragmentShader,
        //   transparent: true,
        lights: true

    });

    /*
  {
    "objects": [
        {
            "obj1": {
                "lightmapIndex": 0,
                "offsetX": -0.2,
                "offsetY": 0.4,
                "scaleX": 0.5,
                "scaleY": 0.6
            }
        },
        {
            "obj2": {
                "lightmapIndex": 0,
                "offsetX": -0.2,
                "offsetY": 0.4,
                "scaleX": 0.5,
                "scaleY": 0.6
            }
        }
    ]
}
*/
    //test
    //var lightmapST = JSON.parse(LightmapST);
    // var loader = new THREE.FBXLoader(manager);
    // loader.load("InteriorOffice/Untitled.fbx", function (object) {
    var loader = new THREE.ObjectLoader();
    loader.load('InteriorOffice/office.json', function (object) {
        object.traverse(function (child) {
            if (child instanceof THREE.Mesh) {

                var oldMat = child.material;
                //oldMat.map = texture;
                // let lightmapMat = new THREE.MeshStandardMaterial();
                // lightmapMat.color = color;
                //oldMat.lightMap = LightmapTex;
                var newMat = lightmapMat.clone();
                 // var newMat = new THREE.MeshStandardMaterial();
                  newMat.diffuse = oldMat.value;
                 //newMat.map = texture;
                 //newMat.lightmap = LightmapTex;
                 //newMat.metalness = 0;
                // newMat.roughness = 1;
                //  newMat.uniforms["mesh_color"].value = oldMat.color;
                newMat.uniforms["diffuse"].value = oldMat.color;
                newMat.uniforms["map"].value = texture;
                newMat.uniforms["lightmapOn"].value = true;
                newMat.uniforms["indirect"].value = 1;
                newMat.uniforms["roughness"].value = 1;
                newMat.uniforms["metalness"].value = 0.9;
                // if (lightmapST[child.name] == null) {
                //    console.log("lightmapST.json: missing object " + child.name);

                //  }
                //  else {
                newMat.uniforms["lightMap"].value = LightmapTex;
                //   var st = new THREE.Vector4(lightmapST[child.name].scaleX, lightmapST[child.name].scaleY,
                //        lightmapST[child.name].offsetX, lightmapST[child.name].offsetY);

                //    newMat.uniforms["lightmapST"].value = st;
                // }

                child.material = newMat;

                //let color = new THREE.Color(Math.random(), Math.random(), Math.random());
                // console.log(color.getHexString());
                child.material.needsUpdate = true;

                child.castShadow = true;
                child.receiveShadow = true;
            }
        });

        mesh = object;
        sceneObj = mesh;
        scene.add(mesh);
        blocker.style.opacity = 0.0;
        /*
                mesh.traverse( function( node )
                {
                    if ( node instanceof THREE.Mesh )
                    {
                        //node.geometry.computeVertexNormals();
                        var oldMat = node.material;
        
                        oldMat.lightMap = LightmapTex;
                        node.material = oldMat;
                    }
        
                    if (node.material)
                    {
                        //node.material.side = THREE.DoubleSide;
                    }
                } );*/

        objBox3 = new THREE.Box3().setFromObject(mesh);

        centerOffset.x = -(objBox3.min.x + objBox3.max.x) * 0.5 * 0.0;
        centerOffset.y = -(objBox3.min.y + objBox3.max.y) * 0.5 * 0.0;
        centerOffset.z = -(objBox3.min.z + objBox3.max.z) * 0.5 * 0.0;

        mesh.translateX(centerOffset.x);
        mesh.translateY(centerOffset.y);
        mesh.translateZ(centerOffset.z);

        console.log("=============Max: " + objBox3.max.x + ", " + objBox3.max.y + ", " + objBox3.max.z);
        console.log("=============Min: " + objBox3.min.x + ", " + objBox3.min.y + ", " + objBox3.min.z);

        //controls.reset();

        camera.position.copy(new THREE.Vector3(7.64, 6.87, -1.30));
        var targetPostition = new THREE.Vector3(4.01, 4.58, -3.17);
        camera.lookAt(targetPostition);
        controls.target.copy(targetPostition);

    }, onProgress, onError);

    var loader = new THREE.ObjectLoader();
    loader.load('InteriorOffice/scene.json', function (obj) {
        mesh = obj;

        //mesh.translateX(centerOffset.x);
        //mesh.translateY(centerOffset.y);
        //mesh.translateZ(centerOffset.z);

        mesh.traverse(function (node) {
            if (node instanceof THREE.Light) {
                //node.geometry.computeVertexNormals();
                console.log("====================== is light: " + node.intensity + ", " + node.distance + ", " + node.name + ", " + node.exponent);

                node.intensity = node.intensity * 1;
                node.distance = 10;

                if (node.name.indexOf("Point_light") >= 0) {

                    node.castShadow = true;
    
                    node.shadow.camera.near = 0.1;
                    node.shadow.camera.far = 40;
                    node.shadow.bias = -0.001;
            
                    node.add(createLightGizmos());
                    Pointlights.push(node);
                    PointlightsDefaultIntensity.push(node.intensity);
                    if (node.name == "Point_light_1") {
                        var transformControl = new THREE.TransformControls( camera, renderer.domElement );
                        transformControl.addEventListener( 'mouseUp', onTransformControllerMove );
                        transformControl.attach(node);
                        scene.add(transformControl);
                        node.children[0].material.color = new THREE.Color("red");
                        guiPars["PlantX"] = node.position.x;
                        guiPars["PlantY"] = node.position.y;
                        guiPars["PlantZ"] = node.position.z;
                        lightObj = node;
                        oldPosValue = node.position;
                    }
                    else
                    {
                        node.castShadow = false;
                    }
                }

                if (node instanceof THREE.DirectionalLight) {
                    node.castShadow = true;
                }
                node.translateX(centerOffset.x);
                node.translateY(centerOffset.y);
                node.translateZ(centerOffset.z);

                if (node.name == "DynLight0") {
                    node.visible = false;

                    dynLightGreen = new THREE.SpotLight(0xFFFFFF);
                    dynLightGreen.position.set(node.position.x, node.position.y, node.position.z);
                    dynLightGreen.distance = node.distance;
                    dynLightGreen.intensity = node.intensity * 3;
                    dynLightGreen.penumbra = 0.5;
                    // node.castShadow = true;
                    // node.shadow.camera.near = 1;
                    // node.shadow.camera.far = 100;
                    //node.shadow.bias =  0.005;
                }

                if (node.name == "DynLight1") {
                    node.visible = false;

                    dynLightRed = new THREE.SpotLight(0xFFFFFF);
                    dynLightRed.position.set(node.position.x, node.position.y, node.position.z);
                    dynLightRed.distance = node.distance;
                    dynLightRed.intensity = node.intensity * 3;
                    dynLightRed.penumbra = 0.5;
                    //dynLightGreen.castShadow = true;

                }
            }
            else {
                /*
                if (node.name == "DynLight0_target") {
                    dynLightGreenTarget = node;
                }

                if (node.name == "DynLight1_target") {
                    dynLightRedTarget = node;
                }*/
            }
        });

        dynLightGreen.target = dynLightGreenTarget;
        dynLightRed.target = dynLightRedTarget;

        //    scene.add(dynLightGreen);
        //    scene.add(dynLightRed);

        scene.add(obj);
    });
};

preInit();
init();
animate();

function sliceBlob(blob, start, end, type) {
    type = type || blob.type;

    if (end !== undefined) {
        return blob.slice(start, end, type);
    }
    else {
        return blob.slice(start);
    }
}

function getImgPixel(img, x, y) {
    console.log("===========img width: " + img.width + ", " + img.height);

    var canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    var context = canvas.getContext('2d');
    context.drawImage(img, 0, 0);

    ///*
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height);

    // Now you can access pixel data from imageData.data.
    // It's a one-dimensional array of RGBA values.
    // Here's an example of how to get a pixel's color at (x,y)
    var x = 256;
    var y = 256;
    var index = (y * imageData.width + x) * 4;
    var red = imageData.data[index];
    var green = imageData.data[index + 1];
    var blue = imageData.data[index + 2];
    var alpha = imageData.data[index + 3];
    //*/

    console.log("========================================= pixel: " + red + ", " + green + ", " + blue);
}

function InitDGI() {
    isUpdatingLightmap = true;
    var msg = new Uint8Array(1);
    msg[0] = StateCode.C2S_DGI_Init;

    //TEST!!!!!!!!!!!
    socket.send(msg);
}

function RecalcLightmap()
{
    isUpdatingLightmap = true;
    var msg = new Uint8Array(1);
    msg[0] = StateCode.C2D_DGI_Recalc;

    //TEST!!!!!!!!!!!
    socket.send(msg);
}
function setupNetwork() {
    try {
        var hostAddress = "ws://" + serverIP + ":" + serverPort + "/" + webService;
        socket = new WebSocket(hostAddress);
    }
    catch (ex) {
        console.log(ex);
    }
    try {
        socket.onopen = function (msg) {
            console.log("succeed to connect！");

            isServerConnected = true;

            InitDGI();
        }

        socket.onmessage = function (msg) {
            var stateCodeBlob = msg.data.slice(0, 2);
            var jsonBlob = msg.data.slice(2);
            var headerReader = new FileReader();
            headerReader.onload = function (e) {
                if (e.target.readyState == FileReader.DONE) {
                    var array = new Uint8Array(e.target.result);
                    var stateCode = array[0];
                    if (stateCode == StateCode.S2C_Json_LightmapArray) {
                        console.log("Set Lightmap Array");
                        var reader = new FileReader();
                        reader.onload = function (e) {
                            var json = JSON.parse(reader.result);
                            lightmapArray = json.lightmap;
                            for (var i = 0; i < lightmapArray.length; ++i) {
                                SetLightmap(i, lightmapArray[i].base64);
                            }
                            isUpdatingLightmap = false;
                            //let lightmapBin = atob(lightmapBase64Str);
                            //  console.log("lightmapBin: "+ lightmapBin);
                            //  console.log("base64: "+ lightmapBase64Str);
                            //engine.SetLightmap(b64DecodeUnicode(lightmapBase64Str));

                        };
                        reader.readAsText(jsonBlob);
                    }
                    else if (stateCode == StateCode.S2C_Json_LightmapST) {
                        console.log("Set LightmapST Do Nothing");

                        /*
                        reader = new FileReader();
                        reader.onload = function (e) {
                            var json = JSON.parse(reader.result);

                            SetLightmapST(json);

                        };
                        reader.readAsText(jsonBlob);
                        */
                    }
                    else {

                        var headerValue = ((array[1] << 8) + array[0]);

                        //resetTimestamps.forEach(function (value, key)
                        //{
                        //    if ((key <= headerValue)) {
                        //        resetTimestamps.delete(key);
                        //    }
                        //});

                        //if (lastReqFrameCounter === headerValue)
                        /*
                        {
                            var readerData = new FileReader();
                            readerData.onload = function (e) {
                                if (e.target.readyState == FileReader.DONE) {
                                    //console.log("========================================= rec data 0: " + e.target.result);
                                    image.src = e.target.result;
 
                                    renderTarget.texture.image = image;
                                    renderTarget.texture.needsUpdate = true;
 
                                    //getImgPixel(renderTarget.texture.image , 256 , 256);
                                }
                            }
                            readerData.readAsDataURL(sliceBlob(msg.data, 2));
                        }
                        */
                    }
                }
            }

            headerReader.readAsArrayBuffer(stateCodeBlob);
        };

        socket.onclose = function (msg) {
            isServerConnected = false;

            window.alert("暂时无法访问服务器，请稍后！");
        };

        socket.onerror = function (msg) {
            isServerConnected = false;

            console.log(msg.data);
        }
    }
    catch (ex) {
        console.log(ex);
    }
}

function nextPowerOfTwo(dstValue) {
    for (var i = 0; i < PowerOfTwoArray.length; ++i) {
        if (PowerOfTwoArray[i] > dstValue) {
            return PowerOfTwoArray[i];
        }
    }

    return MaxRadianceSizeConst;
}

function setupRadianceSize() {
    varMaxRadianceSize = Math.ceil(MaxRadianceSizeConst / MaxProgressiveTime * progressiveCounter);

    //console.log("================================== Size: " + varMaxRadianceSize);
    var scaleRatio = varMaxRadianceSize / window.innerHeight;

    if (window.innerWidth > window.innerHeight) {
        scaleRatio = varMaxRadianceSize / window.innerWidth;
    }
    scaleRatio = THREE.Math.clamp(scaleRatio, 0, 1);

    //radianceWidth = nextPowerOfTwo(window.innerWidth * scaleRatio);
    //radianceHeight = nextPowerOfTwo(window.innerHeight * scaleRatio);

    radianceWidth = Math.ceil(window.innerWidth * scaleRatio);
    radianceHeight = Math.ceil(window.innerHeight * scaleRatio);

    //console.log("================================== Size: " + radianceWidth + ", " + radianceHeight);

    isNeedToSync = true;
}

function preInit() {
    PowerOfTwoArray = new Array();
    PowerOfTwoArray.push(32);
    PowerOfTwoArray.push(64);
    PowerOfTwoArray.push(128);
    PowerOfTwoArray.push(256);
    PowerOfTwoArray.push(512);

    var manager = new THREE.LoadingManager();
    manager.onProgress = function (item, loaded, total) {
        console.log(item, loaded, total);
    };

    var loader = new THREE.ImageLoader(manager);
    loader.load('assets/blackimg.jpg', function (image) {
        blackImage = image;
    });
}

function init() {
    renderer = new THREE.WebGLRenderer({  
        antialias:true,       //是否开启反锯齿   
    });  
    renderer.setPixelRatio(window.devicePixelRatio);

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type=THREE.PCFSoftShadowMap;
    renderer.shadowMap
   // renderer.shadowMap.type = THREE.BaseShadowMap;
    renderer.shadowMap.renderSingleSided = false;
    renderer.setSize(screenWidth, screenHeight);

    setupRadianceSize();

    console.log("============================= screen size: " + radianceWidth + ", " + radianceHeight);

    //renderer.setSize(512 , 512);
    renderer.setClearColor(0x445599);

    document.body.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(60, screenWidth / screenHeight, 3, 1000);

    scene = new THREE.Scene();

    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.damping = 0.2;
    /*
    controls = new THREE.FirstPersonControls(camera, renderer.domElement);
    controls.movementSpeed = 3;
    controls.lookSpeed = 0.1;
    controls.noFly = true;
    controls.mouseDragOn = true;
    controls.lookVertical = true;
    */
    window.addEventListener('resize', onWindowResize, false);
   // window.addEventListener( 'click', onMouseClick, false );
    /*
    var axisHelper = new THREE.AxisHelper( 5 );
    scene.add( axisHelper );
    //*/

    var lightPosition = new THREE.Vector3(0, 3, 0);

    var geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
    var material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
    var cube = new THREE.Mesh(geometry, material);
    cube.position.set(lightPosition.x, lightPosition.y, lightPosition.z);

    //scene.add(cube);

    dynSpotLight = new THREE.SpotLight(0xffffff);
    dynSpotLight.position.set(lightPosition.x, lightPosition.y, lightPosition.z);
    dynSpotLight.angle = THREE.Math.PI * 0.25;
    dynSpotLight.target.position.z = 5;
    dynSpotLight.target.updateMatrixWorld();
    //scene.add(dynSpotLight);

    var ambient = new THREE.AmbientLight(0xffffff);
    //scene.add( ambient );

    var directionalLight = new THREE.DirectionalLight(0xffffff);
    directionalLight.position.set(1, 1, 1);
    //scene.add( directionalLight );


    loadScene('Car');

    setupNetwork();

    initPostProcessing();

    setupGui();
}

function setupGui() {
    var gui = new dat.GUI();
    gui.add(guiPars, "PointLight", 0, 3).name("点光源强度").onChange(function () { changePointLightIntensity(); });
    gui.add(guiPars, "indirect", 0, 3).name("间接光照强度").onChange(function(){changeIndirectIntensity();});
   // gui.add(guiPars, "metalness", 0, 1).name("matelness").onChange(function(){changeMaterialMetalness();});
    //gui.add(guiPars, "roughness", 0, 1).name("roughness").onChange(function(){changeMaterialRoughness();});
  
    //  gui.add(guiPars, "lcrsEnable").name("Enable LCRS").onChange(function () { changeLcrsStatus(); });
   // gui.add(guiPars, "dynLightGreen").name("Green Light").onChange(function () { changeDynLightStatus(); });
   // gui.add(guiPars, "dynLightRed").name("Red Light").onChange(function () { changeDynLightStatus(); });
    gui.add(guiPars, "lightmapOn").name("Lightmap On").onChange(function () { changeLightmapStatus(); });
    //gui.add(guiPars, "PlantX", guiPars.PlantX - 10, guiPars.PlantX + 10).listen().name("LightX").onChange(function () { changePlantPosition(); });//.onFinishChange(function(){onFinishChangePlantPisition();});
    //gui.add(guiPars, "PlantY", guiPars.PlantY - 10, guiPars.PlantY + 10).listen().name("LightY").onChange(function () { changePlantPosition(); });//.onFinishChange(function(){onFinishChangePlantPisition();});
    //gui.add(guiPars, "PlantZ", guiPars.PlantZ - 10, guiPars.PlantZ + 10).listen().name("LightZ").onChange(function () { changePlantPosition(); });//.onFinishChange(function(){onFinishChangePlantPisition();});
    gui.add(guiPars, "onReCalc").name("Recalculate Lightmap");
    gui.open();
}



function changeMaterialMetalness()
{
    sceneObj.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material.uniforms["metalness"].value = guiPars.metalness;
        }
    });
}

function changeMaterialRoughness()
{
    sceneObj.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material.uniforms["roughness"].value = guiPars.roughness;
        }
    });
}
function onClickedReCalcLightmap()
{
    RecalcLightmap();
}
function changePlantPosition() {
    guiPars.PlantX = lightObj.position.x;
    guiPars.PlantY = lightObj.position.y;
    guiPars.PlantZ = lightObj.position.z;
 //   if (lightObj != null) {
  //      lightObj.position.set(guiPars.PlantX, guiPars.PlantY, guiPars.PlantZ);
 //   }
    if (isUpdatingLightmap) return;
 //   if (oldPosValue == null) {
  //      oldPosValue = new THREE.Vector3(guiPars.PlantX, guiPars.PlantY, guiPars.PlantZ);
 //       return;
 //   }
    //设置定时，两秒值不改变则传到后端计算
    //if (!oldPosValue.equals(new THREE.Vector3(guiPars.PlantX, guiPars.PlantY, guiPars.PlantZ))) {
        if (!oldPosValue.equals(lightObj.position)) {
        if (updateTimeOut != null) clearTimeout(updateTimeOut);
        updateTimeOut = setTimeout(function () {
            oldPosValue = null;
            updateTimeOut = null;
            syncPlantDataToServer();
        }, 2000);
    }
    //oldPosValue = new THREE.Vector3(guiPars.PlantX, guiPars.PlantY, guiPars.PlantZ);
    oldPosValue = lightObj.position;
}

function onFinishChangePlantPisition() {

}
function changePointLightIntensity() {
    var mult = guiPars["PointLight"];
    for (var i = 0; i < Pointlights.length; ++i) {
        Pointlights[i].intensity = PointlightsDefaultIntensity[i] * mult
    }
}
function changeLcrsStatus() {
    var lcrsEnable = guiPars["lcrsEnable"];

    if (lcrsEnable) {
        blendPass.uniforms["diffuseVal"].value = 1.0;
        blendPass.uniforms["blendVal"].value = 0.8;
    }
    else {
        blendPass.uniforms["diffuseVal"].value = 1.0;
        blendPass.uniforms["blendVal"].value = 0.0;
    }

    //dynSpotLight.position.x += 1.0;
}

function changeDynLightStatus() {
    console.log("=========================== change light status: " + guiPars["dynLightGreen"]);
    dynLightGreen.visible = guiPars["dynLightGreen"];
    dynLightRed.visible = guiPars["dynLightRed"];

    dynSpotLight.position.x += 1.0;
}

function changeIndirectIntensity()
{
    var on = guiPars["indirect"];
    sceneObj.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material.uniforms["indirect"].value = guiPars.indirect;
        }
    });
}
function changeLightmapStatus() {
    var on = guiPars["lightmapOn"];
    sceneObj.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material.uniforms["lightmapOn"].value = on;
        }
    });
}
function changeBlendRatio() {
    blendPass.uniforms["diffuseVal"].value = guiPars["direct"];
    blendPass.uniforms["blendVal"].value = guiPars["bounce"];
}

function initPostProcessing() {
    var renderPass = new THREE.RenderPass(scene, camera);

    blendPass = new THREE.ShaderPass(THREE.BlendPass);
    blendPass.renderToScreen = true;

    var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBuffer: false };
    //var pars = {minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, stencilBuffer: false};
    renderTarget = new THREE.WebGLRenderTarget(radianceWidth, radianceHeight, pars);
    renderTarget.texture = new THREE.Texture();
    //renderTarget.texture = new THREE.DataTexture(pixels, textureWidth, textureHeight, THREE.RGBAFormat);
    //renderTarget.texture.needsUpdate = true;

    blendPass.uniforms["tBlend"].value = renderTarget;

    effectComposer = new THREE.EffectComposer(renderer);
    effectComposer.addPass(renderPass);
    effectComposer.addPass(blendPass);
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);

    setupRadianceSize();
}

function animate() {
    requestAnimationFrame(animate);

    var deltaTime = clock.getDelta();

    render(deltaTime);

    update(deltaTime);

    checkNeedToSync();

    frameCounter++;
}
function onTransformControllerMove(event)
{
    //if (isUpdatingLightmap) return;
    
    updateTimeOut = setTimeout(function () {
        syncPlantDataToServer();
    }, 2000);
}
function render(deltaTime) {
    //renderer.render(scene, camera);


    effectComposer.render();
}

function update(deltaTime) {
    //console.log("===================== sync state: " + isServerConnected  + ", " + isNeedToSync);

    if (isServerConnected && isNeedToSync) {
        //syncClientDataToServer();
        //prepared = false;

        // destroy the old data at first
        if (isNeedToResetLastGT) {
            //  renderTarget.texture.image = blackImage;
            //  renderTarget.texture.needsUpdate = true;

            //resetTimestamps.set(frameCounter, clock.elapsedTime);
        }
    }

    //resetTimestamps.forEach(function (value , key)
    //{
    //    if (((clock.elapsedTime - value) > samleDuration))
    //    {
    //        renderTarget.texture.image = blackImage;
    //        renderTarget.texture.needsUpdate = true;
    //    }
    //    //console.log("========== " + key + ", " + value + ", delta: " + );
    //});
}

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function (from, to) {
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
};

var time;
function checkNeedToSync() {
    isNeedToSync = false;

    if (lastCameraPosition.distanceToSquared(camera.position) > DiffEPS || 8 * (1 - lastCameraRotation.dot(camera.quaternion)) > DiffEPS ||
        lastLightPosition.distanceToSquared(dynSpotLight.position) > DiffEPS || 8 * (1 - lastLightRotation.dot(dynSpotLight.quaternion)) > DiffEPS) {
        time = clock.elapsedTime;

        if (time - lastSampleTimestamp > samleDuration) {
            lastCameraPosition.copy(camera.position);
            lastCameraRotation.copy(camera.quaternion);

            lastLightPosition.copy(dynSpotLight.position);
            lastLightRotation.copy(dynSpotLight.quaternion);

            isNeedToSync = true;

            isNeedToResetLastGT = true;

            // Reset progressive counter
            progressiveCounter = 1;
        }
    }
    else {
        // Update progressive counter
        if (progressiveCounter < MaxProgressiveTime) {
            time = clock.elapsedTime;

            if (time - lastSampleTimestamp > samleDuration) {
                progressiveCounter++;

                isNeedToSync = true;

                isNeedToResetLastGT = false;
            }
        }
    }

    if (isNeedToSync) {
        setupRadianceSize();
    }
}

function packHeader() {
    return radianceWidth * 2000 + radianceHeight;
}

function syncPlantDataToServer() {
    isUpdatingLightmap = true;
    var PlantJson =
        {
            name: lightObj.name,
            positionX: -lightObj.position.x,
            positionY: lightObj.position.y,
            positionZ: lightObj.position.z,
        };
    var plantJsonStr = JSON.stringify(PlantJson);
    var enc = new TextEncoder("utf-8");
    var jsonUint8 = enc.encode(plantJsonStr);
    var msg = new Uint8Array(1 + jsonUint8.length);

    msg[0] = StateCode.C2S_Json_UpdateObjectPosition;
    msg.set(jsonUint8, 1);
    socket.send(msg);
}
function syncClientDataToServer() {
    var msg = new Float32Array(new ArrayBuffer(56));
    msg[0] = StateCode.C2S_AttributeStream;
    // Pack the size of radiance map at first
    msg[1] = packHeader();

    // Synchronize the position and rotation of camera
    msg[2] = -camera.position.x;
    msg[3] = camera.position.y;
    msg[4] = camera.position.z;

    camera.getWorldDirection(cameraForward);
    msg[5] = -cameraForward.x;//-camera.rotation.x * 57.29578;
    msg[6] = cameraForward.y;//camera.rotation.y * 57.29578;
    msg[7] = cameraForward.z;//camera.rotation.z * 57.29578;

    // Synchronize the position and rotation of main light
    msg[8] = -dynSpotLight.position.x;
    msg[9] = dynSpotLight.position.y;
    msg[10] = dynSpotLight.position.z;

    if (dynLightGreen.visible) {
        msg[8] = 1.0;
    }
    else {
        msg[8] = -1.0;
    }

    if (dynLightRed.visible) {
        msg[9] = 1.0;
    }
    else {
        msg[9] = -1.0;
    }

    if (isNeedToResetLastGT) {
        lastReqFrameCounter = frameCounter;
    }

    msg[10] = lastReqFrameCounter;

    dynSpotLight.getWorldDirection(lightForward);
    msg[11] = -lightForward.x;//-dynSpotLight.rotation.x * 57.29578;
    msg[12] = lightForward.y;//dynSpotLight.rotation.y * 57.29578;
    msg[13] = lightForward.z;//dynSpotLight.rotation.z * 57.29578;

    //time = clock.elapsedTime;
    //console.log("req with: " + radianceWidth + ", " + radianceHeight + ", " + frameCounter);

    lastSampleTimestamp = clock.elapsedTime;

    socket.send(msg);
}
function SetLightmap(i, lightmap) {
    var image = new Image();
    image.src = "data:image/jpeg;base64," + lightmap;
    LightmapTex.image = image;
    LightmapTex.needsUpdate = true;

    sceneObj.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
            child.material.uniforms["lightmap"] = LightmapTex;
        }
    });
}
function SetLightmapST(json) {
    sceneObj.traverse(function (node) {
        if (node instanceof THREE.Mesh) {
            //set st
            var index = json[node.name].lightmapIndex;
            var st = THREE.Vector4(json[node.name].scaleX, json[node.name].scaleY, json[node.name].offsetX, json[node.name].offsetY)
            node.material.uniforms["lightmapST"] = st;

        }
    });

}

function onMouseClick( event ) {
    mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
    raycaster.setFromCamera( mouse, camera );
    // See if the ray from the camera into the world hits one of our meshes
    var intersects = raycaster.intersectObjects( scene );
    // Toggle rotation bool for meshes that we clicked
    if ( intersects.length > 0)
    {
        if(intersects[0].object == raycastObj && enableTransformControl == false) {
            transformControl = new THREE.TransformControls( camera, renderer.domElement );
            transformControl.addEventListener( 'change', render );
            transformControl.attach(lightObj);
            scene.add(transformControl);
            enableTransformControl = true;
        }
        else if(intersects[0].object == transformControl)
        {

        }
        else
        {
            enableTransformControl = false;
            scene.remove(transformControl);
        }
    }
}